package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Scope;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.DataSource;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.DbUtils;
import com.jichangxiu.framework.utils.NoUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import com.jichangxiu.framework.utils.ValidateUtils;
import com.jichangxiu.system.entity.bo.SysSourceBo;
import com.jichangxiu.system.entity.dto.add.AddSysSource;
import com.jichangxiu.system.entity.dto.edit.EditSysSource;
import com.jichangxiu.system.entity.dto.query.QuerySysSource;
import com.jichangxiu.system.entity.po.SysSource;
import com.jichangxiu.system.entity.po.SysTenant;
import com.jichangxiu.system.entity.vo.SysSourceVo;
import com.jichangxiu.system.mapper.SysSourceMapper;
import com.jichangxiu.system.service.SysSourceService;
import com.jichangxiu.system.service.SysTenantService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysSourceServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:46
 */
@Service
public class SysSourceServiceImpl extends BaseServiceImpl<SysSourceMapper, SysSource, SysSourceVo> implements SysSourceService {

    @Resource
    private SysTenantService sysTenantService;

    /**
     * 获取系统数据源列表
     *
     * @return 系统数据源列表
     */
    @Override
    public List<DataSource> sourceList() {
        // 不是根数据源的都加入动态数据源。
        // 因为根数据源是默认已经加入到动态数据源中去了的
        List<SysSource> sysSourceList = list(new LambdaQueryWrapper<SysSource>().eq(SysSource::getEnabled, true));
        if (ObjectUtil.isEmpty(sysSourceList))
            return new ArrayList<>();
        List<SysSourceVo> sysSourceVoList = ConvertUtils.convert(sysSourceList.stream().filter(sysSource -> !Constants.DEFAULT_DATA_SOURCE.equals(sysSource.getCode())).collect(Collectors.toList()), SysSourceVo.class);
        if (ObjectUtil.isNull(sysSourceVoList))
            return new ArrayList<>();
        return sysSourceVoList.stream().map(sysSourceVo -> DataSource.builder().code(sysSourceVo.getCode()).url(sysSourceVo.getUrl()).username(sysSourceVo.getUsername()).password(sysSourceVo.getPassword()).build()).collect(Collectors.toList());
    }

    /**
     * 新增系统数据源
     *
     * @param addSysSource 新增系统数据源视图对象
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysSource addSysSource) {
        return getBaseMapper().insert(addAndEditValidBo(ConvertUtils.convert(addSysSource, SysSourceBo.class))) > 0;
    }

    /**
     * 单（多）选删除系统数据源
     *
     * @param idList 系统数据源主键列表
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        return getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
    }

    /**
     * 修改系统数据源
     *
     * @param editSysSource 修改系统数据源视图对象
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysSource editSysSource) {
        return getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysSource, SysSourceBo.class))) > 0;
    }

    /**
     * 根据主键查询系统数据源
     *
     * @param id       系统数据源主键
     * @param isExtend 是否扩展
     * @return 系统数据源详情
     */
    @Override
    public SysSourceVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysSourceVo sysSourceVo = getBaseMapper().selectJoinOne(SysSourceVo.class, buildQueryWrapper(QuerySysSource.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysSourceVo))
            sysSourceVo = renderSysSourceVoList(ListUtil.toList(sysSourceVo)).get(0);
        return sysSourceVo;
    }

    /**
     * （可分页）查询系统数据源列表
     *
     * @param querySysSource 查询视图对象
     * @return （分页）系统数据源列表
     */
    @Override
    public Page<SysSourceVo> lists(QuerySysSource querySysSource) {
        Page<SysSourceVo> sysSourceVoPage = getBaseMapper().selectJoinPage(startPage(), SysSourceVo.class, buildQueryWrapper(querySysSource));
        if (Boolean.TRUE.equals(querySysSource.getIsExtend()))
            sysSourceVoPage.setRecords(renderSysSourceVoList(sysSourceVoPage.getRecords()));
        return sysSourceVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysSourceBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysSource addAndEditValidBo(SysSourceBo sysSourceBo) {
        SysSource sysSource = ConvertUtils.convert(sysSourceBo, SysSource.class);
        if (ObjectUtil.isNull(sysSource))
            throw new ServiceException("待校验对象为空");
        if (ObjectUtil.isEmpty(sysSource.getCode()))
            throw new ServiceException("标识不能为空");
        if (ObjectUtil.isEmpty(sysSource.getName()))
            throw new ServiceException("名称不能为空");
        if (ObjectUtil.isEmpty(sysSource.getType()))
            throw new ServiceException("类型不能为空");
        if (ObjectUtil.isEmpty(sysSource.getUrl()))
            throw new ServiceException("链接不能为空");
        if (ObjectUtil.isEmpty(sysSource.getUsername()))
            throw new ServiceException("用户不能为空");
        if (ObjectUtil.isEmpty(sysSource.getPassword()))
            throw new ServiceException("密码不能为空");
        if (!DbUtils.checkConnection(sysSource.getType(), sysSource.getUrl(), sysSource.getUsername(), sysSource.getPassword()))
            throw new ServiceException("请检查链接有效性");
        List<ValidateBos> validateBosList = Stream.of(
                ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysSource::getId, sysSource.getId(), "主键")),
                ValidateUtils.createOrEqValidateBos(
                        ValidateUtils.createValidateBo(SysSource::getNo, sysSource.getNo(), "编码"),
                        ValidateUtils.createValidateBo(SysSource::getCode, sysSource.getCode(), "标识"),
                        ValidateUtils.createValidateBo(SysSource::getName, sysSource.getName(), "名称"),
                        ValidateUtils.createValidateBo(SysSource::getRemark, sysSource.getRemark(), "备注"))
        ).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysSource.getId())) {
            // 新增
            if (Constants.DEFAULT_DATA_SOURCE.equals(sysSource.getCode()))
                throw new ServiceException("标识不能为 " + Constants.DEFAULT_DATA_SOURCE);
            sysSource.setNo(NoUtils.createNo(SecurityUtils.getTenantId(), "source"));
            return sysSource;
        } else {
            // 修改
            SysSource dbSysSource = getById(sysSource.getId());
            if (Constants.DEFAULT_DATA_SOURCE.equals(sysSource.getCode()) && !sysSource.getCode().equals(dbSysSource.getCode()))
                throw new ServiceException("标识不能为 " + Constants.DEFAULT_DATA_SOURCE);
            if (!dbSysSource.getEnabled().equals(sysSource.getEnabled()) && Constants.DEFAULT_DATA_SOURCE.equals(sysSource.getCode()))
                throw new ServiceException("标识为 " + Constants.DEFAULT_DATA_SOURCE + " 的源状态不能修改");
            if (sysTenantService.count(new LambdaQueryWrapper<SysTenant>().in(SysTenant::getSourceId, sysSource.getId())) > 0)
                throw new ServiceException("数据源被租户关联，不能修改");
            // 当修改了启用状态，并且是不启用得情况下
            if (!sysSource.getEnabled().equals(dbSysSource.getEnabled()) && !sysSource.getEnabled())
                // 如果被用户关联不能修改状态
                if (sysTenantService.count(new LambdaQueryWrapper<SysTenant>().in(SysTenant::getSourceId, sysSource.getId())) > 0)
                    throw new ServiceException(sysSource.getName() + "该数据源已被租户关联，不能修改状态为禁用");
            BeanUtil.copyProperties(sysSource, dbSysSource, true);
            return dbSysSource;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isEmpty(idList))
            throw new ServiceException("待校验主键列表为空");
        List<SysSourceBo> sysSourceBoList = selectJoinList(SysSourceBo.class, buildQueryWrapper(QuerySysSource.builder().idList(idList).build()));
        if (sysTenantService.count(new LambdaQueryWrapper<SysTenant>().in(SysTenant::getSourceId, idList)) > 0)
            throw new ServiceException("尚有数据源被租户关联，不能删除");
        sysSourceBoList.forEach(sysSourceBo -> {
            if (Constants.DEFAULT_DATA_SOURCE.equals(sysSourceBo.getCode()))
                throw new ServiceException("标识为 " + Constants.DEFAULT_DATA_SOURCE + " 的数据源不能删除");
        });
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysSourceVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysSourceVo> renderSysSourceVoList(List<SysSourceVo> sysSourceVoList) {
        if (ObjectUtil.isNotEmpty(sysSourceVoList)) {
            // todo 扩展信息填入
        }
        return sysSourceVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysSource 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysSource> buildQueryWrapper(QuerySysSource querySysSource) {
        MPJLambdaWrapper<SysSource> lambdaQueryWrapper = new MPJLambdaWrapper<SysSource>().selectAll(SysSource.class);
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getId()), SysSource::getId, querySysSource.getId());
        lambdaQueryWrapper.in(ObjectUtil.isNotEmpty(querySysSource.getIdList()), SysSource::getId, querySysSource.getIdList());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getNo()), SysSource::getNo, querySysSource.getNo());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysSource.getCode()), SysSource::getCode, querySysSource.getCode());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysSource.getName()), SysSource::getName, querySysSource.getName());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getType()), SysSource::getType, querySysSource.getType());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getUrl()), SysSource::getUrl, querySysSource.getUrl());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysSource.getUsername()), SysSource::getUsername, querySysSource.getUsername());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getPassword()), SysSource::getPassword, querySysSource.getPassword());
        lambdaQueryWrapper.like(ObjectUtil.isNotEmpty(querySysSource.getEncode()), SysSource::getEncode, querySysSource.getEncode());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysSource.getSort()), SysSource::getSort, querySysSource.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysSource.getRemark()), SysSource::getRemark, querySysSource.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysSource.getEnabled()), SysSource::getEnabled, querySysSource.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysSource.getEnabledBy()), SysSource::getEnabledBy, querySysSource.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysSource.getEnabledTime()), SysSource::getEnabledTime, querySysSource.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysSource.getScope()), querySysSource.getScope());
        lambdaQueryWrapper.orderByAsc(SysSource::getSort);
        return lambdaQueryWrapper;
    }

}